﻿using System;
using System.Collections.Generic;
using System.Configuration;
using System.Diagnostics;
using System.Linq;
using System.Text;

namespace Workflows.Components.Entities.Distances.FieldDistances
{
    /// <summary>
    /// similarity = Math.Max(0, 1 - Math.Pow(Math.Max(0, (d-Diameter)),2) / Diameter)
    /// </summary>
    public class RangeSimilarityComparer:IAttributeDistanceCalcutor
    {
        private double _Diameter;
        /// <summary></summary>
        public double Diameter
        {
            get
            {
                return this._Diameter;
            }
            set
            {
                this._Diameter = value;
            }
        }

        public RangeSimilarityComparer()
        {
            this._ComparerSettings = new Dictionary<string, string>();

            this._ComparerSettings.Add("RangeDiameter", "10");
        }

        #region Implementation of IAttributeComparer

        /// <summary>
        /// load dictionary into memory or read config settings from file
        /// </summary>
        public void InitInMemorySettings()
        {
            if (this._ComparerSettings.ContainsKey("RangeDiameter"))
                this._Diameter = double.Parse(this._ComparerSettings["RangeDiameter"]);
            else
                this._Diameter = 10;
        }

        private Dictionary<string, string> _ComparerSettings;
        public Dictionary<string, string> ComparerSettings
        {
            get
            {
                return _ComparerSettings;
            }
            set
            {
                this._ComparerSettings = value;

            }
        }

        /// <summary>
        /// return similarity score in [0,1]
        /// 0 indicate no similarity or null match
        /// 1 indicate perfect match
        /// </summary>
        /// <param name="attrValue1"></param>
        /// <param name="attrValue2"></param>
        /// <param name="result"></param>
        /// <returns></returns>
        public double Compare(object attrValue1, object attrValue2, ref MatchResult result)
        {
            result = MatchResult.NullMatch;
            if (attrValue1 == null || attrValue2 == null)
                return 0;

            string strVal1 = attrValue1.ToString();
            string strVal2 = attrValue2.ToString();
            if (strVal1.Length == 0 || strVal2.Length == 0)
                return 0;

            double dbl1 = double.Parse(strVal1.ToString());
            double dbl2 = double.Parse(strVal2.ToString());
            double distance = Math.Abs(dbl2 - dbl1);

            double similarity = Math.Max(0, 1 - Math.Pow(Math.Max(0, (distance - Diameter)), 2) / Diameter);
            return similarity;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="entity1"></param>
        /// <param name="entity2"></param>
        /// <param name="attrMetaData"></param>
        /// <param name="result"></param>
        /// <returns></returns>
        public double Compare(Entity entity1, Entity entity2, AttributeMetaData attrMetaData, ref MatchResult result)
        {
            
            Debug.Assert(!string.IsNullOrEmpty(attrMetaData.AttributeName));

            string attrName = attrMetaData.AttributeName;
            object attrValue1 = null;
            object attrValue2 = null;
            if (entity1.ComparableAttributes.ContainsKey(attrName))
                attrValue1 = entity1.ComparableAttributes[attrName];
            if (entity2.ComparableAttributes.ContainsKey(attrName))
                attrValue2 = entity2.ComparableAttributes[attrName];
            return this.Compare(attrValue1, attrValue2, ref result);
        }

        /// <summary>
        /// argument to bind attribute name
        /// </summary>
        public string[] ArgumentNames
        {
            get
            {
                return new string[] { "RangeArgument" };
            }
        }

        #endregion
    }
}
